﻿Option Strict On

Namespace Translation

  ''' <summary>
  ''' Represents a lexical tree structure.
  ''' </summary>
  ''' <remarks>This class cannot be inherited.</remarks>
  Public NotInheritable Class Node

    Private _Key As String
    Private _InstanceKey As String
    Private _IsRegular As Boolean = False
    Private _RegularMap As SortedDictionary(Of Char, String)
    Private _Value As String
    Private _Parent As Node
    Private _Children As List(Of Node)
    Private _LastAcceptingChildIndex As Integer = -1

    ''' <summary>
    ''' Gets a parent of current node.
    ''' </summary>
    ''' <value>The parent link of current node.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property Parent As Node
      Get
        Return _Parent
      End Get
    End Property

    ''' <summary>
    ''' Gets a value that indicates whether the current node has a value.
    ''' </summary>
    ''' <value>True if the current node has a value; otherwise, false.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property IsHaltAccept As Boolean
      Get
        Return Not String.IsNullOrEmpty(_Value)
      End Get
    End Property

    ''' <summary>
    ''' Gets a value that indicates whether the current node is a root of the tree.
    ''' </summary>
    ''' <value>True if the current node has no parent; otherwise, false.</value>
    ''' <remarks></remarks>
    Public ReadOnly Property IsRoot As Boolean
      Get
        Return _Parent Is Nothing
      End Get
    End Property

    ''' <summary>
    ''' Initializes a node with specified key-value.
    ''' </summary>
    ''' <param name="Key">The key of node to be created.</param>
    ''' <param name="Value">The value of node to be created.</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal Key As String, ByVal Value As String)
      MyClass.New(Key, Value, Nothing)
    End Sub

    ''' <summary>
    ''' Initializes a node with specified <seealso cref="KeyValuePair(Of String, String)"/>.
    ''' </summary>
    ''' <param name="Pair">The <see cref="KeyValuePair(Of String, String)"/> to be created.</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal Pair As KeyValuePair(Of String, String))
      MyClass.New(Pair.Key, Pair.Value)
    End Sub

    ''' <summary>
    ''' Initializes a regular node with specified key-value and regular sets.
    ''' </summary>
    ''' <param name="Key">The regular key of node to be created.</param>
    ''' <param name="Value">The regular value of node to be created.</param>
    ''' <param name="RegularSets">The regular sets.</param>
    ''' <exception cref="System.FormatException">The key is not contain a colon sign or a name of regular set is undefined.</exception>
    ''' <remarks>The key must be in the form of [A:B], where A is a name of regular set and B is an identifier that appeared in the value.</remarks>
    Public Sub New(ByVal Key As String, ByVal Value As String,
                   ByVal RegularSets As SortedDictionary(Of String, SortedDictionary(Of Char, String)))
      'Assigns object data.
      _Key = Key
      _Value = Value
      _IsRegular = RegularSets IsNot Nothing

      'Checks and adjusts a regular key.
      If _IsRegular Then
        Dim ColonIndex As Integer = Key.LastIndexOf(":")
        If ColonIndex >= 0 Then
          'Extracts only a name of regular set.
          Key = Key.Remove(ColonIndex).Remove(0, 1).Trim
        Else
          Throw New FormatException("A regular node must contain a colon sign legally")
        End If

        If RegularSets.ContainsKey(Key) Then
          _RegularMap = RegularSets.Item(Key)
        Else
          Throw New FormatException("Found an undefined regular set")
        End If

        'Reassigns a regular key.
        _Key = _Key.Remove(1, _Key.IndexOf(":"))
      End If

      'Creates an empty list of children node.
      _Children = New List(Of Node)(3)
    End Sub

    ''' <summary>
    ''' Adds a specified node to be a child of current node.
    ''' </summary>
    ''' <param name="Child">The child node to be added.</param>
    ''' <returns>The child node that was added.</returns>
    ''' <remarks>If the child node is logically exists in the children list, then a child node in the list will be returned.</remarks>
    Public Function AddChild(ByVal Child As Node) As Node
      'Finds a logically equivalent child node.
      For i As Integer = 0 To _Children.Count - 1
        If _Children(i)._Key.Equals(Child._Key) AndAlso _Children(i)._IsRegular = Child._IsRegular Then
          If Not _Children(i).IsHaltAccept And Child.IsHaltAccept Then
            'Merges value data from a specified child node.
            _Children(i)._Value = Child._Value
          End If
          'Returns an exists child node.
          Return _Children(i)
        End If
      Next

      'Returns a specified child node.
      Child._Parent = Me
      _Children.Add(Child)
      Return Child
    End Function

    ''' <summary>
    ''' Finds an accecpting child with specified input key.
    ''' </summary>
    ''' <param name="InputKey">An input key.</param>
    ''' <param name="AcceptingChild">Nothing or any node.</param>
    ''' <returns>True if there is one more child accpets an input key; otherwise, false.</returns>
    ''' <remarks>If this method returns true then AcceptingChild will be referenced to a child that accepts this input key; otherwise, AcceptingChild will not be changed.</remarks>
    Public Function ChildAccepts(ByVal InputKey As Char, ByRef AcceptingChild As Node) As Boolean
      'Finds a matched key.
      'Starts a sequential searching from the next of last accepting child index.
      For i As Integer = _LastAcceptingChildIndex + 1 To _Children.Count - 1
        With _Children(i)
          'Exactly match a key.
          If ._Key.Equals(InputKey) Then
            ._InstanceKey = InputKey
            _LastAcceptingChildIndex = i
            AcceptingChild = _Children(i)
            Return True

            'Probably match a regular key.
          ElseIf ._IsRegular Then
            'Determines whether an input key is contains in a regular set.
            Dim Code As String = Nothing
            If ._RegularMap.TryGetValue(InputKey, Code) Then
              ._InstanceKey = Code
              _LastAcceptingChildIndex = i
              AcceptingChild = _Children(i)
              Return True
            End If
          End If
        End With
      Next

      'Not found matched key.
      Return False
    End Function

    ''' <summary>
    ''' Resets a last accepting child index that affect to <seealso cref="ChildAccepts"/> method.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub ResetState()
      _LastAcceptingChildIndex = -1
    End Sub

    ''' <summary>
    ''' Gets a <seealso cref="System.Text.StringBuilder">StringBuilder</seealso> that contains a value or a regular expression of the current node.
    ''' </summary>
    ''' <param name="Replacement">Nothing or a regular expression related to the current node.</param>
    ''' <returns>A value or a regular expression of the current node.</returns>
    ''' <exception cref="System.InvalidOperationException">If the current node is not halt accept.</exception>
    ''' <remarks></remarks>
    Public Function GetValue(ByVal Replacement As System.Text.StringBuilder) As System.Text.StringBuilder
      'Determines whether a replacement is nothing.
      If Replacement Is Nothing Then
        If _Value Is Nothing Then
          Throw New InvalidOperationException
        Else
          'Assigns a value of the current node.
          Replacement = New System.Text.StringBuilder(_Value)
        End If
      End If

      'Returns a concrete value or a regular expression.
      If _IsRegular Then
        'Replaces any matched key with an instance key of the current node.
        Return Replacement.Replace(_Key, _InstanceKey)
      Else
        Return Replacement
      End If
    End Function

    Public Overrides Function GetHashCode() As Integer
      Return _Key.GetHashCode
    End Function

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
      If TypeOf obj Is Node Then
        Dim AnotherNode As Node = CType(obj, Node)
        Return _Key.Equals(AnotherNode._Key) AndAlso
          _IsRegular = AnotherNode._IsRegular AndAlso
          Object.ReferenceEquals(_RegularMap, AnotherNode._RegularMap)
      Else
        Return False
      End If
    End Function

    Public Overrides Function ToString() As String
      Return _Key
    End Function

  End Class
End Namespace